Software developers often build software applications on top of a “runtime” library such as Microsoft's Common Language Runtime (CLR), or runtime libraries like Microsoft's C-Language Runtime (CRT). Runtimes may provide common functions such as application startup logic, as well as class libraries for handling common programming challenges. For example, the CLR contains class libraries that implement common data structures (e.g., linked lists, hash tables, and so forth), as well as common functionality, such as garbage collection. By using a runtime, an application relieves the application's author from implementing well-understood programming logic so that the author can focus on logic specific to the problem the author is trying to solve.
Applications have grown complex, often allowing extendibility through third-party software plug-ins. For example, a word processing application may invoke third-party software modules that add new items to toolbars and menus, or provide other extended functionality to the word processing application. As the application evolves and the application author updates the application, it is common for the application to be built to run with one runtime version while the application's author would like to continue supporting older plug-ins built using an earlier runtime version. Similarly, the plug-ins may evolve faster than the hosting application, and the plug-ins may rely on a newer version of a runtime while the application relies on an older version of the runtime. A newer runtime version may introduce intentional and unintentional changes in behavior that can negatively affect components that operated correctly on the previous version. Further complicating such applications is that managed execution environments, such as Microsoft's CLR, may allow a mix of managed and native (non-managed) application code to run in the same process, each having its own runtime considerations and expectations.
Managed code is computer program code that executes under the management of a virtual machine or runtime, unlike unmanaged code, which is executed directly by the computer's central processing unit (CPU). Programs in any programming language can, in principle, be compiled into either managed or unmanaged code. In practice, however, each programming language is typically compiled into one type. For example, the Java programming language is almost always compiled into managed code, although there are Java compilers that can generate unmanaged code (such as the GNU Compiler for Java). By contrast, Microsoft's Visual C++ development environment can produce both managed code (running under the .NET CLR) or unmanaged code.
Designers of runtimes may initially design the runtime so that a particular process can only load a single version of that runtime, and application authors often build applications with the same assumption. When the runtime author updates and distributes a newer version of the runtime, older applications are sometimes forced to execute using the new runtime potentially leading to backwards compatibility problems. For example, if a user deploys an application compiled against runtime version 1 to a computer that has runtime version 2 installed, the application may run using the installed version, which can be subtly incompatible with the application and result in a variety of problems during execution. Solving this problem by maintaining total backwards compatibility is not feasible for any complex and evolving runtime, so such runtimes may evolve to support multiple versions loaded simultaneously into the same process at once so that application code can use the runtime version for which it was designed.
Another problem is that when evolving a runtime to support multiple versions “side-by-side” in the same process, it is often desirable to continue running applications correctly that were designed for a previous version of the runtime that did not support multiple runtime versions in a single application process. The runtime may include functions that query information about the application execution environment from which it is desirable to hide new versions of the runtime. At the same time, the runtime author wants to make it easy to update an application to run against a new version of the runtime (e.g., for compatibility testing or just updating). These two goals are contradictory. These considerations can be translated into the following question: how can an application designed to have only one runtime in its process execute in a multiple runtime process with no impact, while also making it easy to move applications forward to a new version of the runtime?
A previous simple solution to this problem is to keep applications tightly coupled to the version of the runtime they were designed for, so that all requests to a runtime are implicitly targeted at a specific version with no sharing across versions. This is the solution taken by the Microsoft CRT. The biggest downsides are that application authors cannot move applications between versions without recompiling and applications designed for different runtime versions are limited in how they can share information.